Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / testing / src / commonTest / kotlin / org / meshtastic / core / testing / FakeBleServiceFailureInjectionTest.kt

Displaying Raw • Download

core/testing/src/commonTest/kotlin/org/meshtastic/core/testing/FakeBleServiceFailureInjectionTest.kt 41414f832f938b655c97bc07509bc140ea16e2a3 (41414f83) Text, 6.01 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.testing

Tff7b72import T7ee787kotlinx.coroutines.flow.collect
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.launch
Tff7b72import T7ee787kotlinx.coroutines.test.runTest
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.meshtastic.core.ble.BleCharacteristic
Tff7b72import T7ee787org.meshtastic.core.ble.BleWriteType
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertFailsWith
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertNotNull
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72import T7ee787kotlin.uuid.Uuid

Tff7b72class T56d364FakeBleServiceFailureInjectionTest Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3char Tff7b72= Te6edf3BleCharacteristicTb4b4b4(Te6edf3UuidTb4b4b4.Te6edf3randomTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72private Tff7b72val Te6edf3service Tff7b72= Te6edf3FakeBleServiceTb4b4b4(Tb4b4b4)

Tf0883e@Test
Tff7b72fun Td2a8ffwriteExceptionIsPersistentTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3writeException Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffwrite-failTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3ex1 Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3charTb4b4b4, Te6edf3ByteArrayTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4, Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSETb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3ex1Tb4b4b4.Te6edf3message Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffwrite-failTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffExpected write-fail messageTa5d6ff"Tb4b4b4)
T8b949e// Prove persistence — second write also throws because exception persists
Tff7b72val Te6edf3ex2 Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3charTb4b4b4, Te6edf3ByteArrayTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4, Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSETb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3ex2Tb4b4b4.Te6edf3message Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffwrite-failTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffExpected write-fail message on second callTa5d6ff"Tb4b4b4)
T8b949e// tests must explicitly clear it
Te6edf3serviceTb4b4b4.Te6edf3writeException Tff7b72= Tff7b72null
Te6edf3serviceTb4b4b4.Te6edf3writeTb4b4b4(Te6edf3charTb4b4b4, Te6edf3ByteArrayTb4b4b4(T79c0ff2Tb4b4b4)Tb4b4b4, Te6edf3BleWriteTypeTb4b4b4.Te6edf3WITH_RESPONSETb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3serviceTb4b4b4.Te6edf3writesTb4b4b4.Te6edf3size Tff7b72=Tff7b72= T79c0ff1Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffreadExceptionIsThrownAndResetTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3readException Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffread-failTa5d6ff"Tb4b4b4)
Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3readTb4b4b4(Te6edf3charTb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3serviceTb4b4b4.Te6edf3readTb4b4b4(Te6edf3charTb4b4b4)Tb4b4b4.Te6edf3isEmptyTb4b4b4(Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffRead should return empty after exception resetTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveExceptionCausesFlowExceptionTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Te6edf3serviceTb4b4b4.Te6edf3observeException Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffobserve-failTa5d6ff"Tb4b4b4)
Tff7b72val Te6edf3flow Tff7b72= Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4)
Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3flowTb4b4b4.Te6edf3collectTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveExceptionByCharacteristicOnlyFailsTargetCharacteristicTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72val Te6edf3otherChar Tff7b72= Te6edf3BleCharacteristicTb4b4b4(Te6edf3UuidTb4b4b4.Te6edf3randomTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3serviceTb4b4b4.Te6edf3observeExceptionsByCharacteristicTff7b72[Te6edf3charTb4b4b4.Te6edf3uuidTff7b72] Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6fftarget-observe-failTa5d6ff"Tb4b4b4)

Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4)Tb4b4b4.Te6edf3collectTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3exTb4b4b4.Te6edf3message Tff7b72=Tff7b72= Ta5d6ff"Ta5d6fftarget-observe-failTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffExpected target observe failureTa5d6ff"Tb4b4b4)

Tff7b72var Te6edf3subscribed Tff7b72= Tff7b72false
Te6edf3serviceTb4b4b4.Te6edf3emitNotificationTb4b4b4(Te6edf3otherCharTb4b4b4.Te6edf3uuidTb4b4b4, Te6edf3byteArrayOfTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4)
Te6edf3withTimeoutOrNullTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4) Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3otherCharTb4b4b4) Tb4b4b4{ Te6edf3subscribed Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4.Te6edf3firstTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3assertTrueTb4b4b4(Te6edf3subscribedTb4b4b4, Ta5d6ff"Ta5d6ffOther characteristic should still subscribe normallyTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveBeforeSubscriptionExceptionDoesNotInvokeOnSubscriptionTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72var Te6edf3subscribed Tff7b72= Tff7b72false
Te6edf3serviceTb4b4b4.Te6edf3observeBeforeSubscriptionExceptionByCharacteristicTff7b72[Te6edf3charTb4b4b4.Te6edf3uuidTff7b72] Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffbefore-subscribe-failTa5d6ff"Tb4b4b4)

Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4) Tb4b4b4{ Te6edf3subscribed Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4.Te6edf3collectTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3exTb4b4b4.Te6edf3message Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffbefore-subscribe-failTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffExpected before-subscription failureTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3subscribedTb4b4b4, Ta5d6ff"Ta5d6ffonSubscription must not run before the injected failureTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveExceptionByCharacteristicInTwoArgAlsoThrowsBeforeSubscriptionTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
T8b949e// observeExceptionsByCharacteristic is the 1-arg seam, but the 2-arg override must also treat it as a
T8b949e// pre-readiness failure — otherwise the default onStart wrap would invoke onSubscription before the throw.
Tff7b72var Te6edf3subscribed Tff7b72= Tff7b72false
Te6edf3serviceTb4b4b4.Te6edf3observeExceptionsByCharacteristicTff7b72[Te6edf3charTb4b4b4.Te6edf3uuidTff7b72] Tff7b72= Te6edf3ExceptionTb4b4b4(Ta5d6ff"Ta5d6ffshared-observe-failTa5d6ff"Tb4b4b4)

Tff7b72val Te6edf3ex Tff7b72= Te6edf3assertFailsWithTff7b72<Te6edf3ExceptionTff7b72> Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4) Tb4b4b4{ Te6edf3subscribed Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4.Te6edf3collectTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3assertTrueTb4b4b4(Te6edf3exTb4b4b4.Te6edf3message Tff7b72=Tff7b72= Ta5d6ff"Ta5d6ffshared-observe-failTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffExpected the shared characteristic exceptionTa5d6ff"Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3subscribedTb4b4b4, Ta5d6ff"Ta5d6ffonSubscription must not run before a shared characteristic failureTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveNeverSubscribeDoesNotInvokeOnSubscriptionTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
Tff7b72var Te6edf3subscribed Tff7b72= Tff7b72false
Te6edf3serviceTb4b4b4.Te6edf3observeNeverSubscribeCharacteristics Tff7b72+Tff7b72= Te6edf3charTb4b4b4.Te6edf3uuid

Te6edf3withTimeoutOrNullTb4b4b4(T79c0ff1T79c0ff0T79c0ff0Tb4b4b4) Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4) Tb4b4b4{ Te6edf3subscribed Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4.Te6edf3collectTb4b4b4(Tb4b4b4) Tb4b4b4}

Te6edf3assertFalseTb4b4b4(Te6edf3subscribedTb4b4b4, Ta5d6ff"Ta5d6ffonSubscription must not run for never-subscribe characteristicTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tf0883e@Test
Tff7b72fun Td2a8ffobserveNeverSubscribeStillExposesNotificationsTb4b4b4(Tb4b4b4) Tff7b72= Te6edf3runTest Tb4b4b4{
T8b949e// Even though onSubscription is suppressed, the returned flow is the bare SharedFlow, so emitNotification
T8b949e// must still reach active collectors.
Te6edf3serviceTb4b4b4.Te6edf3observeNeverSubscribeCharacteristics Tff7b72+Tff7b72= Te6edf3charTb4b4b4.Te6edf3uuid
Tff7b72var Te6edf3subscribed Tff7b72= Tff7b72false
Tff7b72var Te6edf3receivedTb4b4b4: Te6edf3ByteArray? Tff7b72= Tff7b72null

Tff7b72val Te6edf3collector Tff7b72= Te6edf3launch Tb4b4b4{ Te6edf3serviceTb4b4b4.Te6edf3observeTb4b4b4(Te6edf3charTb4b4b4) Tb4b4b4{ Te6edf3subscribed Tff7b72= Tff7b72true Tb4b4b4}Tb4b4b4.Te6edf3collect Tb4b4b4{ Te6edf3received Tff7b72= Tffa657it Tb4b4b4} Tb4b4b4}
Te6edf3testSchedulerTb4b4b4.Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)
Te6edf3serviceTb4b4b4.Te6edf3emitNotificationTb4b4b4(Te6edf3charTb4b4b4.Te6edf3uuidTb4b4b4, Te6edf3byteArrayOfTb4b4b4(T79c0ff1Tb4b4b4, T79c0ff2Tb4b4b4, T79c0ff3Tb4b4b4)Tb4b4b4)
Te6edf3testSchedulerTb4b4b4.Te6edf3advanceUntilIdleTb4b4b4(Tb4b4b4)
Te6edf3collectorTb4b4b4.Te6edf3cancelTb4b4b4(Tb4b4b4)

Te6edf3assertFalseTb4b4b4(Te6edf3subscribedTb4b4b4, Ta5d6ff"Ta5d6ffonSubscription must not run for never-subscribe characteristicTa5d6ff"Tb4b4b4)
Te6edf3assertNotNullTb4b4b4(Te6edf3receivedTb4b4b4, Ta5d6ff"Ta5d6ffNotifications must still flow through the bare SharedFlowTa5d6ff"Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3receivedTff7b72!!Tb4b4b4.Te6edf3contentEqualsTb4b4b4(Te6edf3byteArrayOfTb4b4b4(T79c0ff1Tb4b4b4, T79c0ff2Tb4b4b4, T79c0ff3Tb4b4b4)Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffNotification payload must be exposed verbatimTa5d6ff"Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.07s